use std::collections::HashMap;
use std::fs;
use std::fs::File;
use std::io::{Read, Write};
use std::path::Path;
use std::time::SystemTime;
use zip::write::FileOptions;
use zip::ZipWriter;

#[derive(serde::Serialize)]
pub struct FileMetadata {
    name: String,
    file_type: FileType,
    modified_time: u64,
}

#[derive(serde::Serialize)]
pub enum FileType {
    File,
    Directory,
}

#[tauri::command]
pub fn exists(path: std::path::PathBuf) -> bool {
    fs::metadata(path).is_ok()
}

#[tauri::command]
pub fn delete_directory(directory_path: String) -> Result<(), String> {
    let path = Path::new(&directory_path);
    if path.is_dir() {
        fs::remove_dir_all(path).map_err(|e| e.to_string())?;
    }
    Ok(())
}

#[tauri::command]
pub fn metadata(path: std::path::PathBuf) -> Result<FileMetadata, String> {
    let metadata = match fs::metadata(&path) {
        Ok(metadata) => metadata,
        Err(err) => return Err(format!("Failed to get file metadata: {}", err)),
    };

    let name = match path.file_name() {
        Some(name) => name.to_string_lossy().to_string(),
        None => return Err("Failed to get file name".to_string()),
    };

    let file_type = if metadata.is_dir() {
        FileType::Directory
    } else {
        FileType::File
    };

    let modified_time = match metadata.modified() {
        Ok(modified_time) => modified_time,
        Err(err) => return Err(format!("Failed to get file modification time: {}", err)),
    };

    let modified_time = match modified_time.duration_since(SystemTime::UNIX_EPOCH) {
        Ok(duration) => duration.as_secs(),
        Err(_) => return Err("Failed to convert modification time to seconds since Unix epoch".to_string()),
    };

    Ok(FileMetadata {
        name,
        file_type,
        modified_time,
    })
}

#[tauri::command]
pub fn read_file(path: std::path::PathBuf) -> Result<Vec<u8>, String> {
    match fs::read(path) {
        Ok(data) => Ok(data),
        Err(err) => Err(format!("Failed to read file: {}", err)),
    }
}

#[tauri::command]
pub fn read_dir(path: std::path::PathBuf) -> Result<Vec<std::path::PathBuf>, String> {
    match fs::read_dir(&path) {
        Ok(entries) => {
            let paths: Vec<std::path::PathBuf> = entries
                .filter_map(|entry| {
                    entry.ok().map(|dir_entry| dir_entry.path())
                })
                .collect();
            Ok(paths)
        }
        Err(err) => Err(format!("Failed to read directory: {}", err)),
    }
}

#[tauri::command]
pub fn compress_directory(source_path: String, target_path: String) -> Result<(), String> {
    let source_path = Path::new(&source_path);
    let target_path = Path::new(&target_path);

    let target_file = File::create(target_path).map_err(|e| e.to_string())?;
    let mut zip = ZipWriter::new(target_file);

    add_directory_to_zip(source_path, &mut zip, source_path)?;

    Ok(())
}


fn add_directory_to_zip(
    base_path: &Path,
    zip: &mut ZipWriter<File>,
    dir: &Path,
) -> Result<(), String> {
    for entry in dir.read_dir().map_err(|e| e.to_string())? {
        let entry = entry.map_err(|e| e.to_string())?;
        let path = entry.path();

        if path.is_dir() {
            add_directory_to_zip(base_path, zip, &path)?;
        } else {
            let mut file = File::open(&path).map_err(|e| e.to_string())?;
            let relative_path = path.strip_prefix(base_path).map_err(|e| e.to_string())?;

            zip.start_file_from_path(relative_path, FileOptions::default())
                .map_err(|e| e.to_string())?;
            let mut buffer = Vec::new();
            file.read_to_end(&mut buffer).map_err(|e| e.to_string())?;
            zip.write_all(&buffer).map_err(|e| e.to_string())?;
        }
    }

    Ok(())
}

#[tauri::command]
pub fn extract_zip(zip_path: String, target_path: String) -> Result<(), String> {
    let zip_path = Path::new(&zip_path);
    let target_path = Path::new(&target_path);

    let file = File::open(zip_path).map_err(|e| e.to_string())?;
    let mut zip = zip::ZipArchive::new(file).map_err(|e| e.to_string())?;

    for i in 0..zip.len() {
        let mut file = zip.by_index(i).map_err(|e| e.to_string())?;
        let out_path = target_path.join(file.sanitized_name());

        if (&*file.name()).ends_with('/') {
            fs::create_dir_all(&out_path).map_err(|e| e.to_string())?;
        } else {
            if let Some(p) = out_path.parent() {
                if !p.exists() {
                    fs::create_dir_all(&p).map_err(|e| e.to_string())?;
                }
            }

            let mut outfile = File::create(&out_path).map_err(|e| e.to_string())?;
            std::io::copy(&mut file, &mut outfile).map_err(|e| e.to_string())?;
        }
    }

    Ok(())
}

#[tauri::command]
pub fn test_method(test_field: String, test_list: Vec<i32>) -> (String, Vec<i32>) {
    println!("test_field:{test_field:?}");
    println!("test_list:{test_list:?}");
    (test_field, test_list)
}